/*
 * Log.java	2008/07/28
 *
 * Copyright 2008 Web & Zen Multimedia, All rights reserved.
 * Use is subject to license terms.
 */
package it.wzm.lightweightlog;


import java.util.Properties;


/**
 * <p>
 * The main accessor for logging our entry. It implements a singleton design.
 * So there is only one instance of Log in the whole application, by calling the Log.getLog() method,
 * a check is made to see if an instance of Log already exists. If yes it is returned, if not it is initialized.
 * </p>
 * <p>
 * By calling:<br/>
 * Log l = Log.getLog();<br/>
 * l.info(Object);<br/>
 * </p>
 * <p>
 * a property file, dump.properties is searched in the classpath then if not found the defaul one inside package will be taken.
 * Then...<br/>
 * -Transform file into properties<br/>
 * -Load global property<br/>
 * -Find class name of the Dumper to initialize<br/>
 * -Extrapolate property for dumper<br/>
 * -Initialize the Queue<br/>
 * -Start the QueueDumper if necessary<br/>
 *
 * @author Leonardo Celati
 * @version 1.0
 */
@SuppressWarnings("unchecked")
public class Log implements Loggable {

    /**
     * Service log
     */
    private static boolean serviceLogEnabled;
    
    /**
     * Status
     */
    public enum Status {
    	INIT,
    	RUNNING,
    	FAILURE,
    	SHUTDOWN,
    	DEFAULT,
    }
    
    /**
     * Current status of log
     */
    private Status status;
    
    /**
     * Build a default log
     */
    Log() {
    	// building a default set of properties...
    	Properties p = new Properties();
    	p.setProperty("servicelog", String.valueOf(Constant.DEFAULT_SERVICELOG));
    	// init log...
    	this.setStatus(Status.DEFAULT);
    	this.init(p);
    }
    
	/**
     * Default constructor
     */
    Log(Properties p) {
    	// now..we init our log...
    	this.init(p);
    	// this will be useful later on...
    	// because it is searched by the QueueDumper Thread
    	Thread.currentThread().setName("Log");    	
    }
    
    /**
     * Get status
     * @param status
     */
    private void setStatus(Status status) {
		this.status = status;
	}

	public Status getStatus() {
		return status;
	}

	/**
     * Is service log enabled ?
     * @return true or false
     */
    public static boolean isServiceLogEnabled() {
		return serviceLogEnabled;
	}

    /**
     * Preinit...
     */
    void preinit(){
    }
    
    /**
     * Private method for init our Log
     */
    protected void init(Properties p) {
    	
    	// basic assert...
    	assert(p != null && !p.isEmpty()): "Property null or empty";

    	// perform pre init if required...
    	this.preinit();
        
        // see if service log is enabled
        if (p.getProperty(PROP_ROOT + "servicelog") != null) {
        	try {
        		serviceLogEnabled = Boolean.parseBoolean(p.getProperty(PROP_ROOT + "servicelog"));
        	} catch (Exception e) {
        		// we do not rethrow error...
        		serviceLogEnabled = DEFAULT_SERVICELOG;
        		this.setStatus(Status.FAILURE);
        	}
        } else {
            serviceLogEnabled = DEFAULT_SERVICELOG;
        }
        
        // post init if required...
        this.postinit();


    }
    
    /**
     * Postinit...
     */
    void postinit() {    	
    }
    
    /**
     * Get all debug level
     * @param tolog The object to be logged
     */
    public void debug(Object tolog) {
        this.add(tolog, new Throwable(), Severity.DEBUG);
    }

    /**
     * Get all info level
     * @param tolog The object to be logged
     */
    public void info(Object tolog) {
        this.add(tolog, new Throwable(), Severity.INFO);
    }

    /**
     * Get all warn level
     * @param tolog The object to be logged
     */
    public void warn(Object tolog) {
        this.add(tolog, new Throwable(), Severity.WARN);
    }

    /**
     * Get all error level
     * @param tolog The object to be logged
     */
    public void error(Object tolog) {
        this.add(tolog, new Throwable(), Severity.ERROR);
    }

    /**
     * Get all fatal level
     * @param tolog The object to be logged
     */
    public void fatal(Object tolog) {
        this.add(tolog, new Throwable(), Severity.FATAL);
    }

    /**
     * Build an Entry from the passed Object and Store it in Queue
     * @param tolog Object to log
     * @param severity The level of Severity
     * @param t A throwable for getting the caller of this error
     */
    void add(Object tolog, Throwable t, Severity severity) {
        // log holder
        Entry e = null;
        // decide if we want to log a string or an exception
        if (tolog != null && tolog instanceof Exception) {
            e = new Entry(new Exception((Exception) tolog), severity, System.currentTimeMillis());
        } else {
            e = new Entry(tolog, severity, t.getStackTrace()[t.getStackTrace().length - 1].getClassName(), System.currentTimeMillis());
        }

        // in service log...
        serviceLog(e.getLine());
    }

    /**
     * Facility for logging everything passed, which will be written to System.err. Useful for debugging.
     * @param o The object to write to log
     */
    public void serviceLog(Object o) {
    	
        // basic check...
        if (!serviceLogEnabled) {
            return;
        }
        // building our entry
        Entry e;
        // check if we want to log instance of Exception or not...
        if (o != null && o instanceof Exception) {
            e = new Entry(new Exception((Exception) o), Severity.SERVICE, System.currentTimeMillis());
        } else {
            Throwable t = new Throwable();
            e = new Entry(o, Severity.SERVICE, t.getStackTrace()[t.getStackTrace().length - 1].getClassName(), System.currentTimeMillis());
        }
        // finally logging
        serviceLog(e);
    }

    /**
     * Facility for logging a passed  Entry which will be written to System.err. Useful for debugging.
     * @param o The object to write to log
     */
    private static <E extends Entry> void serviceLog(E e) {
        System.err.print(e.getLine());
    }
 
}